﻿using System.Drawing;
using System.Runtime.InteropServices;
using static Om.Camera.IRSDK;
using Timer = System.Timers.Timer;

namespace Om.Camera;

public class IrCamera : IBaseCamera
{
    public event EventHandler<bool>? ConnectionChanged;
    private bool _isConnected;
    public bool IsConnected
    {
        get => _isConnected;
        private set
        {
            if (_isConnected != value)
            {
                _isConnected = value;
                ConnectionChanged?.Invoke(this,value);
            }
        }
    }

    // 事件：当新帧到达时触发（用于UI呈现）
    public event Action<byte[]> FrameReceived;

    public string Id { get; private set; }

    public async Task<List<string>> GetCameraList()
    {
        var list = new List<string>();
        try
        {
            for (var i = 0; i < CAMERA_COUNT; i++)
            {
                var ptrIp = new IntPtr(_ptrIpBytes.ToInt64() + Marshal.SizeOf(typeof(T_IPADDR)) * i);
                var ipObj = (T_IPADDR)Marshal.PtrToStructure(ptrIp, typeof(T_IPADDR));

                if (string.IsNullOrWhiteSpace(ipObj.IPAddr))
                    break;

                var ipStr = $"{ipObj.IPAddr}:{ipObj.DataPort}";
                if (list.Contains(ipStr) == false)
                    list.Add(ipStr);
            }
        }
        catch (Exception e)
        {
        }

        return list;
    }

    public Task Connect(string cameraIpOrName)
    {
        var ipAddress = cameraIpOrName;
        var ipArray = ipAddress.Split(':');
        var strIp = ipArray[0].Trim();
        var port = int.Parse(ipArray[1]);

        // 配置IP地址
        var ipAddr = new T_IPADDR
        {
            IPAddr = strIp,
            DataPort = port,
            isValid = 1
        };

        frameProc = FrameProc;
        // 创建设备实例
        var result = IRSDK_Create(
            _handle,
            ipAddr,
            frameProc, // 帧数据回调
            null,
            null
        );
        // 连接相机
        result = IRSDK_Connect(_handle);
        IRSDK_ParamCfg(_handle, T_PARAMTYPE.paramDownSample, 4f); //根据需要降频

        Id = cameraIpOrName;
        IsConnected = true;
        return Task.CompletedTask;
    }

    public Task Disconnect()
    {
        if (IsConnected)
        {
            IRSDK_Stop(_handle);
            IRSDK_Destroy(_handle);
            IRSDK_Quit();
            IsConnected = false;
        }

        return Task.CompletedTask;
    }


    public async Task Capture(string fileName)
    {
        if (!IsConnected)
            throw new Exception("相机未连接");

        var res = IRSDK_SaveFrame2Jpeg(fileName, input_data_copy, _ptrRgb, 0, ref sobj);
    }

    public bool CanSetExposure => false;
    public async Task<bool> SetAutoExposure(bool isAutoExposure)
    {
        return false;
    }

    public async Task<bool> SetExposureValue(int val)
    {
        return false;
    }

    public async Task<int> GetExposureValue()
    {
        return 0;
    }


    /// <summary>
    /// 获取对应点的温度
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <returns></returns>
    public async Task<TemperatureModel> GetPointTemperature(double x, double y)
    {
        IRSDK.STAT_POINT sPt = new();
        sPt.sPoint.x = (ushort)x;
        sPt.sPoint.y = (ushort)y;
        sPt.inputEmiss = 0.98f;
        sPt.inputDis = 2;
        sPt.inputReflect = 20;
        sPt.inputOffset = 0;
        IRSDK.IRSDK_GetPointTemp(input_data_copy, ref sPt, 0);

        var res = new TemperatureModel
        {
            Min = Math.Round(sPt.sTemp.minTemper,1),
            Max = Math.Round(sPt.sTemp.maxTemper,1),
            Avg = Math.Round(sPt.sTemp.avgTemper, 1)
        };

        return res;
    }

    #region MyRegion

    private static readonly int CAMERA_COUNT = 10;
    private readonly int _handle;

    private Frame _currentFrame;
    private IntPtr _rgbBuffer;


    private readonly byte u8Constrast = 50;
    private readonly byte u8Bright = 50;

    private readonly IntPtr _ptrGary;
    private readonly short[] _dataGary = new short[tempSize];

    private readonly IntPtr _ptrRgb;
    private readonly byte[] _dataRgb = new byte[tempSize * 3];

    private readonly IntPtr _ptrBmp;
    private readonly byte[] _dataBmp = new byte[tempSize * 4];

    private readonly IntPtr _ptrBmpLen;
    private readonly int[] _dataBmpLen = new int[1];

    private readonly IntPtr _ptrIpBytes;
    private readonly byte[] _dataIpBytes = new byte[32 * Marshal.SizeOf(typeof(T_IPADDR))];

    private Point screenPoint;
    private Point mousePoint;
    private CBF_IR frameProc;

    private T_IPADDR tIPadd;
    public const int tempSize = 640 * 512 * 2;
    public const int headSize = 32;
    private readonly byte[] head_data = new byte[headSize + tempSize];

    private Frame pFrame;
    private STAT_TEMPER full_Temper = new();
    private STAT_RECT sRectStat;
    private STAT_POLYGON spoly;

    private readonly IntPtr input_data_copy = Marshal.AllocHGlobal(headSize + tempSize);
    private IntPtr input_data;
    private int length = 32;
    private T_IPADDR[] _t_ipaddr;


    public int uint32IPindex = 0;
    private STAT_OBJ sobj;

    private Timer mytime;

    private bool flag = false;
    private readonly IntPtr sFrameRelayPtr = Marshal.AllocHGlobal(headSize + tempSize);
    private readonly Frame sReplayFrame = new();
    private uint replay_index = 0;

    private T_SAVE_HEAD sHead = new();

    private readonly byte paltype = 0;
    private readonly byte palindex = 1;



    public IrCamera()
    {
        try
        {
            GCHandle.Alloc(_dataGary, GCHandleType.Pinned);
            _ptrGary = Marshal.UnsafeAddrOfPinnedArrayElement(_dataGary, 0);
            GCHandle.Alloc(_dataRgb, GCHandleType.Pinned);
            _ptrRgb = Marshal.UnsafeAddrOfPinnedArrayElement(_dataRgb, 0);
            GCHandle.Alloc(_dataBmp, GCHandleType.Pinned);
            _ptrBmp = Marshal.UnsafeAddrOfPinnedArrayElement(_dataBmp, 0);
            GCHandle.Alloc(_dataBmpLen, GCHandleType.Pinned);
            _ptrBmpLen = Marshal.UnsafeAddrOfPinnedArrayElement(_dataBmpLen, 0);
            GCHandle.Alloc(_dataIpBytes, GCHandleType.Pinned);
            _ptrIpBytes = Marshal.UnsafeAddrOfPinnedArrayElement(_dataIpBytes, 0);

            Marshal.StructureToPtr(sReplayFrame, sFrameRelayPtr, true);
            spoly.sPolygon.Pt = new T_POINT[16];


            // 初始化SDK
            IRSDK_Init();
            _handle = 0; // 假设单设备，handle固定为0
            IRSDK_SetIPAddrArray(_ptrIpBytes);
        }
        catch (Exception e)
        {
            throw new Exception($"初始化IrCamera相机失败，{e.Message}");
        }
    }

    // 帧数据回调（非托管 -> 托管）
    private int FrameProc(IntPtr hFrame, IntPtr lParam)
    {
        input_data = hFrame;
        Marshal.Copy(input_data, head_data, 0, headSize + tempSize);

        pFrame = BytesToStruct<Frame>(head_data);

        Marshal.StructureToPtr(pFrame, input_data_copy, false);
        IRSDK_Frame2Gray(input_data_copy, _ptrGary, u8Constrast, u8Bright, 0);

        IRSDK_Gray2Rgb(_ptrGary, _ptrRgb, pFrame.width, pFrame.height, paltype, palindex);
        IRSDK_Rgb2Bmp(_ptrBmp, _ptrBmpLen, _ptrRgb, pFrame.width, pFrame.height);


        FrameReceived?.Invoke(_dataBmp);
        return 1;
    }

    private T BytesToStruct<T>(byte[] bytes) where T : struct
    {
        var obj = new T();

        var size = Marshal.SizeOf(obj);

        // 如果结构体对象的字节数大于所给byte数组的长度，则返回空
        if (size > bytes.Length) return default;

        var structPtr = Marshal.AllocHGlobal(size);
        Marshal.Copy(bytes, 0, structPtr, size);
        var tempObj = Marshal.PtrToStructure(structPtr, obj.GetType());
        Marshal.FreeHGlobal(structPtr);

        return (T)tempObj;
    }

    #endregion
}